#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

struct stm32mp157_led{
	int gpio_num;
	int * gpio;
};

struct stm32mp157_led * led;

int led_drv_open(struct inode * node, struct file * filp)
{
	int i;
	char buff[256];
	int ret;
	for(i = 0; i < led->gpio_num; i++)
	{
		memset(buff, 0x00, sizeof(buff));
		sprintf(buff, "stm32mp157_led%d", i+1);
		ret = gpio_request(led->gpio[i], buff);
		if(ret < 0)
		{
			printk("gpio_request error\n");
			goto gpio_request_err;
		}
		gpio_direction_output(led->gpio[i], 0);
	}
	return 0;
	gpio_request_err:
		for(; i > 0; i--)
		{
			gpio_free(led->gpio[i-1]);
		}
	return ret;
}

int led_drv_release(struct inode * node, struct file * filp)
{
	int i;
	for(i = 0; i < led->gpio_num; i++)
	{
		gpio_free(led->gpio[i]);
	}
	return 0;
}

ssize_t led_drv_write(struct file * filp, const char __user * buff, size_t size, loff_t * pos)
{
	int ret;
	int led_cmd;
	int i;
	ret = copy_from_user(&led_cmd, buff, size);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		return -EAGAIN;
	}
	for(i = 0; i < led->gpio_num; i++)
	{
		if(led_cmd == 0)
			gpio_set_value(led->gpio[i], 0);
		else
			gpio_set_value(led->gpio[i], 0);
	}
	return 0;
}


const struct file_operations led_fops = {
	.write = led_drv_write,
	.open = led_drv_open,
	.release = led_drv_release,
};


struct miscdevice led_misc = {
	.minor = 199,
	.name = "led_drv",
	.fops = &led_fops,
};

int led_drv_probe(struct platform_device * pdev)
{
	int ret;
	int i;
	int temp_gpio;
	//1. 创建全局的结构体对象
	led = kzalloc(sizeof(struct stm32mp157_led), GFP_KERNEL);
	if(led == NULL)
	{
		printk("kzalloc error\n");
		return -ENOMEM;
	}

	//2. 注册杂项设备
	ret = misc_register(&led_misc);
	if(ret < 0)
	{
		printk("misc_register error\n");
		ret = -EINVAL;
		goto kzalloc_err;
	}

	//3. 硬件初始化
	//获取GPIO个数
	led->gpio_num = of_gpio_count(pdev->dev.of_node);
	if(led->gpio_num < 0)
	{
		printk("of_gpio_count error\n");
		ret = led->gpio_num;
		goto misc_register_err;
	}
	//分配空间存储GPIO号
	led->gpio = kzalloc(sizeof(int) * led->gpio_num, GFP_KERNEL);
	if(led->gpio == NULL)
	{
		printk("kzalloc gpio error\n");
	    ret = -ENOMEM;
		goto misc_register_err;
	}
	//获取GPIO号
	for(i = 0; i < led->gpio_num; i++)
	{
		temp_gpio = of_get_gpio(pdev->dev.of_node, i);
		if(temp_gpio < 0)
		{
			printk("of_get_gpio error\n");
	    	ret = temp_gpio;
			goto kzalloc_gpio_err;
		}
		led->gpio[i] = temp_gpio;
	}
	return 0;

	kzalloc_gpio_err:
		kfree(led->gpio);
	misc_register_err:
		misc_deregister(&led_misc);
	kzalloc_err:
		kfree(led);
	return ret;
}

int led_drv_remove(struct platform_device * pdev)
{
	kfree(led->gpio);
	misc_deregister(&led_misc);
	kfree(led);
	return 0;
}

const struct of_device_id led_table[] = {
	{ .compatible = "stm32mp157_led_test2", },
	{}
};

struct platform_driver led_drv = {
	.probe = led_drv_probe,
	.remove = led_drv_remove,
	.driver = {
		.name = "stm32mp157_led_test",
		.of_match_table = led_table,
	},
};

static int __init led_drv_init(void)
{
	return platform_driver_register(&led_drv);
}

static void __exit led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

